import os

import aphw._aphw as impl

import aphw
from aphw.util import return_status

@return_status
def _open(env, filename, flags, params):
    return impl.open_env(env, filename, flags, params)

@return_status
def _create(env, filename, flags, params):
    if filename is None:
        return impl.create_env_memory(env, flags, params)
    else:
        return impl.create_env(env, filename, flags, params)

@return_status
def _close(env, flags):
    return impl.close_env(env, flags)

def _create_db(env, name, flags):
    (err, db) = impl.create_db_env(env, name, flags)
    err = aphw.StatusCodes.values[err]
    return (err, db)

def _open_db(env, name, flags):
    (err, db) = impl.open_db_env(env, name, flags)
    err = aphw.StatusCodes.values[err]
    return (err, db)

@return_status
def _rename_db(env, oldname, newname, flags):
    return impl.rename_db_env(env, oldname, newname, flags)

@return_status
def _erase_db(env, name, flags):
    return impl.erase_db_env(env, name, flags)

def _database_names(env):
    (err, list) = impl.database_names_env(env)
    err = aphw.StatusCodes.values[err]
    return (err, list)

@return_status
def _enable_encryption(env, key, flags):
    return impl.enable_encryption(env, key, flags)

class Environment(object):
    
    def __init__(self, 
                 filename=None,
                 write_through=False,
                 disable_mmap=False,
                 cache_size_policy=aphw.CacheSizePolicy.NORMAL,
                 enable_recovery=False,
                 enable_transactions=True,
                 read_only=False,
                 params=None):
        self._env = impl.Environment()

        if params is None:
            params = {}

        if filename is None or not os.path.exists(filename):
            self.__read_only = False
            err = self._create(filename=filename,
                               write_through=write_through,
                               disable_mmap=disable_mmap,
                               cache_size_policy=cache_size_policy,
                               enable_recovery=enable_recovery,
                               enable_transactions=enable_transactions,
                               params=params)
        else:
            self.__read_only = read_only
            err = self._open(filename=filename,
                             # etc
                             )

        # If there was any error other than not-found, 
        # abort the construction
        if err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(err, 'Error opening database file')


#         if not os.path.exists(filename):
#             temp_env = impl.Environment()
#             err = _create(temp_env, filename, flags)
#             if err != aphw.StatusCodes.SUCCESS:
#                 raise aphw.StatusError(err, 'Error creating Environment')
#             _close(temp_env, 0)

#         if enable_transactions:
#             flags |= aphw.Flags.ENABLE_TRANSACTIONS
#         if read_only:
#             flags |= aphw.Flags.READ_ONLY

#         self._env = impl.Environment()

#         err = _open(self._env, filename, flags)
        
#         if err != aphw.StatusCodes.SUCCESS:
#             raise aphw.StatusError(err, 'Error opening environment file')

    def _create(self,
                filename=None,
                write_through=False,
                disable_mmap=False,
                cache_size_policy = aphw.CacheSizePolicy.NORMAL,
                enable_recovery=False,
                enable_transactions=True,
                params=None):
        flags = 0
        
        if write_through:
            flags |= aphw.Flags.WRITE_THROUGH
        if disable_mmap:
            flags |= aphw.Flags.DISABLE_MMAP
        if enable_recovery:
            if filename is None:
                raise ValueError('Memory databases not allowed with enable-recovery')
            if write_through:
                raise ValueError('write-through not allowed with enable-recovery')
            flags |= aphw.Flags.ENABLE_RECOVERY

        if cache_size_policy == aphw.CacheSizePolicy.STRICT:
            flags |= aphw.Flags.CACHE_STRICT
        elif cache_size_policy == aphw.CacheSizePolicy.UNLIMITED:
            flags |= aphw.Flags.CACHE_UNLIMITED

        if filename is None:
            flags |= aphw.Flags.IN_MEMORY_DB

            if cache_size_policy == aphw.CacheSizePolicy.STRICT:
                raise ValueError('Memory environments not allowed with cache-size-strict')
            
            try:
                if params[aphw.Params.CACHE_SIZE] != 0:
                    raise ValueError('Memory database can not have a cachesize param other than 0')
            except KeyError:
                pass

        return _create(self._env,
                       filename,
                       flags,
                       params)

    def _open(self,
              filename,
              read_only,
              write_through,
              disable_mmap,
              cache_size_policy,
              enable_recovery,
              enable_auto_recovery,
              enable_transactions,
              params):
        flags = 0
        
        if read_only:
            flags |= aphw.Flags.READ_ONLY
        if write_through:
            flags |= aphw.Flags.WRITE_THROUGH
        if disable_mmap:
            flags |= aphw.Flags.DISABLE_MMAP
        if enable_auto_recovery:
            flags |= aphw.Flags.ENABLE_AUTO_RECOVERY
            enable_recovery = True
        if enable_recovery:
            if write_through:
                raise ValueError('write-through not allowed with enable-recovery')
            flags |= aphw.Flags.ENABLE_RECOVERY
        if enable_transactions:
            flags |= aphw.Flags.ENABLE_TRANSACTIONS

        if cache_size_policy == aphw.CacheSizePolicy.STRICT:
            flags |= aphw.Flags.CACHE_STRICT
        elif cache_size_policy == aphw.CacheSizePolicy.UNLIMITED:
            flags |= aphw.Flags.CACHE_UNLIMITED

        return _open(self._env,
                     filename,
                     flags,
                     params)

    def close(self):
        err = _close(self._env,
                     aphw.Flags.TXN_AUTO_ABORT)
        assert err == aphw.StatusCodes.SUCCESS
        
    def create_db(self, 
                  name,
                  record_number=False,
                  flags=0):
        if record_number:
            flags |= aphw.Flags.RECORD_NUMBER

        (err, db) = _create_db(self._env,
                               name,
                               flags)
        
        if err == aphw.StatusCodes.DATABASE_ALREADY_EXISTS:
            raise KeyError('Database named %s already exists in environment' % name)

        elif err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error creating database in environment')

        return aphw.Database(db=db)

    def open_db(self,
                name,
                create=False,
                record_number=False,
                flags=0):
        (err, db) = _open_db(self._env,
                             name,
                             flags)

        if err == aphw.StatusCodes.DATABASE_NOT_FOUND:
            if not create:
                raise KeyError('No database named %s in environment' % name)
            else:
                # TODO: What about incompatible flags?
                return self.create_db(name, flags)

        elif err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error opening database in environment')

        return aphw.Database(db=db)

    def rename_db(self,
                  oldname,
                  newname,
                  flags=0):
        err = _rename_db(self._env,
                         oldname,
                         newname,
                         flags)

        if err == aphw.StatusCodes.DATABASE_NOT_FOUND:
            raise KeyError('No database named %s in environment' % oldname)

        elif err == aphw.StatusCodes.DATABASE_ALREADY_EXISTS:
            raise KeyError('Database named %s already exists in environment' % newname)

        elif err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error renaming database')
    
    def erase_db(self,
                 name,
                 flags=0):
        err = _erase_db(self._env,
                        name,
                        flags)

        if err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error erasing database')

    def database_names(self):
        (err, names) = _database_names(self._env)
        if err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error fetching database names')
        return names
                       
    def enable_encryption(self,
                          key,
                          flags=0):
        err = _enable_encryption(self._env,
                                 key,
                                 flags)
        if err != aphw.StatusCodes.SUCCESS:
            raise aphw.StatusError(
                err,
                'Error enabling encryption')
